package com.example.jdbcdemo.util;

import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.NamedParameterUtils;
import org.springframework.jdbc.core.namedparam.ParsedSql;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.lang.Nullable;

import java.sql.ResultSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

public class NamedTemplateUtil {

    private volatile int cacheLimit = 256;

    private final Map<String, ParsedSql> parsedSqlCache;

    public NamedTemplateUtil() {
        class NamelessClass_1 extends LinkedHashMap<String, ParsedSql> {
            NamelessClass_1(int x0, float x1, boolean x2) {
                super(x0, x1, x2);
            }

            protected boolean removeEldestEntry(Map.Entry<String, ParsedSql> eldest) {
                return this.size() > NamedTemplateUtil.this.getCacheLimit();
            }
        }

        this.parsedSqlCache = new NamelessClass_1(256, 0.75F, true);
    }

    public PreparedStatementCreator getPreparedStatementCreator(String sql, SqlParameterSource paramSource) {
        return this.getPreparedStatementCreator(sql, paramSource, (Consumer) null);
    }

    public PreparedStatementCreator getPreparedStatementCreator(String sql, SqlParameterSource paramSource,
                                                                @Nullable Consumer<PreparedStatementCreatorFactory> customizer) {
        ParsedSql parsedSql = this.getParsedSql(sql);
        PreparedStatementCreatorFactory pscf = this.getPreparedStatementCreatorFactory(parsedSql, paramSource);
        if (customizer != null) {
            customizer.accept(pscf);
        }

        Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, (List) null);
        return pscf.newPreparedStatementCreator(params);
    }

    public ParsedSql getParsedSql(String sql) {
        if (this.getCacheLimit() <= 0) {
            return NamedParameterUtils.parseSqlStatement(sql);
        } else {
            synchronized (this.parsedSqlCache) {
                ParsedSql parsedSql = this.parsedSqlCache.get(sql);
                if (parsedSql == null) {
                    parsedSql = NamedParameterUtils.parseSqlStatement(sql);
                    this.parsedSqlCache.put(sql, parsedSql);
                }

                return parsedSql;
            }
        }
    }

    public int getCacheLimit() {
        return this.cacheLimit;
    }

    public PreparedStatementCreatorFactory getPreparedStatementCreatorFactory(ParsedSql parsedSql, SqlParameterSource paramSource) {
        String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
        List<SqlParameter> declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource);
        PreparedStatementCreatorFactory factory = new PreparedStatementCreatorFactory(sqlToUse, declaredParameters);
        factory.setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
        factory.setUpdatableResults(false);
        return factory;
    }
}

